#!/usr/bin/env python
import sys
sys.path.append('/home/symbol/Dropbox/ncsu_mimo/lib')
sys.path.append('/home/symbol/Dropbox/ncsu_mimo/swig')
sys.path.append('/home/symbol/Dropbox/ncsu_mimo/lib/.libs')
sys.path.append('/home/symbol/Dropbox/ncsu_mimo/swig/.libs')
import ncsu_mimo_swig

from gnuradio import gr, gru, packet_utils
from gnuradio import usrp
from gnuradio import eng_notation
from gnuradio.blks2impl import psk

import gnuradio.gr.gr_threading as _threading

import random
import struct
import sys

class mini_rx(gr.hier_block2):

  def __init__(self, callback, infile):
    gr.hier_block2.__init__(self, "mini_rx",
			        gr.io_signature(0, 0, 0), # Input signature
			        gr.io_signature(0, 0, 0))       # Output signature

    self._samples_per_symbol = 2 
    self.bits_per_symbol = 1

    arity = pow(2,self.bits_per_symbol)

    # find closest constellation point
    rot = 1
    rotated_const = map(lambda pt: pt * rot, psk.constellation[arity])
    self.slicer = gr.constellation_decoder_cb(rotated_const, range(arity))

    self.symbol_mapper = gr.map_bb(psk.gray_to_binary[arity])
        
    # unpack the k bit vector into a stream of bits
    self.unpack = gr.unpack_k_bits_bb(self.bits_per_symbol)

    self.file_source = gr.file_source(gr.sizeof_gr_complex *1, infile)
   
    self.diffdec = gr.diff_phasor_cc()
    #self.connect(self.file_source, self.slicer, self.symbol_mapper, self.unpack)
    self.connect(self.file_source, self.diffdec, self.slicer, self.symbol_mapper, self.unpack)

    self._access_code = packet_utils.default_access_code

    threshold = 12              # FIXME raise exception

    self._rcvd_pktq = gr.msg_queue()          # holds packets from the PHY
    self.correlator = gr.correlate_access_code_bb(self._access_code, threshold)

    self.estimator = ncsu_mimo_swig.estimator_bc(self._access_code, 12)

    self.framer_sink = gr.framer_sink_1(self._rcvd_pktq)
    self.connect(self.unpack, self.correlator, self.framer_sink)
    
    self.connect(self.unpack,      (self.estimator, 0))
    self.connect(self.diffdec,     (self.estimator, 1))
    self.connect(self.file_source, (self.estimator, 2))

    self.file_sink = gr.file_sink(gr.sizeof_gr_complex*1, 
                        "/home/symbol/data/rx_before_agc.dat")
    self.connect(self.estimator, self.file_sink)
    self._watcher = _queue_watcher_thread(self._rcvd_pktq, callback)


class _queue_watcher_thread(_threading.Thread):
    def __init__(self, rcvd_pktq, callback):
        _threading.Thread.__init__(self)
        self.setDaemon(1)
        self.rcvd_pktq = rcvd_pktq
        self.callback = callback
        self.keep_running = True
        self.start()


    def run(self):
        while self.keep_running:
            msg = self.rcvd_pktq.delete_head()
            ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1()))
            if self.callback:
                self.callback(ok, payload)

class my_top_block(gr.top_block):
    def __init__(self,rx_callback, infile):
        gr.top_block.__init__(self)

        # Set up receive path
        self.rxpath = mini_rx(rx_callback, infile)
        self.connect(self.rxpath)

# /////////////////////////////////////////////////////////////////////////////
#                                   main
# /////////////////////////////////////////////////////////////////////////////

global n_rcvd, n_right

def main():
    if len(sys.argv) != 2:
        print 'Usage: sudo ./mini_rx.py input_file'
        sys.exit(2)
    
    infile = sys.argv[1]
    
    global n_rcvd, n_right

    n_rcvd = 0
    n_right = 0
    
    def rx_callback(ok, payload):
        global n_rcvd, n_right
        (pktno,) = struct.unpack('!H', payload[0:2])
        n_rcvd += 1
        if ok:
            n_right += 1

        print "ok = %5s  pktno = %4d  n_rcvd = %4d  n_right = %4d" % (
            ok, pktno, n_rcvd, n_right)


    # build the graph
    tb = my_top_block(rx_callback, infile)

    r = gr.enable_realtime_scheduling()
    if r != gr.RT_OK:
        print "Warning: Failed to enable realtime scheduling."

    tb.start()        # start flow graph
    tb.wait()         # wait for it to finish

if __name__ == '__main__':
    try:
        main()
    except KeyboardInterrupt:
        pass
